home *** CD-ROM | disk | FTP | other *** search
/ The X-Philes (2nd Revision) / The X-Philes Number 1 (1995).iso / xphiles / hp48_2 / chip.ass < prev    next >
Text File  |  1995-03-23  |  16KB  |  378 lines

  1. Article 2648 of comp.sys.handhelds:
  2. Path: en.ecn.purdue.edu!noose.ecn.purdue.edu!samsung!zaphod.mps.ohio-state.edu!sol.ctr.columbia.edu!ira.uka.de!smurf!wrkof!dis.incom.de!disys!gilles
  3. From: gilles@disys.dis.incom.de (Gilles Kohl)
  4. Newsgroups: comp.sys.handhelds
  5. Subject: A CHIP8 Assembler for the HP48SX [long]
  6. Keywords: CHIP8,Assembler,Native
  7. Message-ID: <1990Nov30.105032.19153@disys.dis.incom.de>
  8. Date: 30 Nov 90 10:50:32 GMT
  9. Organization: Delta Information Systems
  10. Lines: 364
  11.  
  12.  
  13. Summary:
  14. --------
  15.  
  16. This posting describes (and contains) an assembler for Andreas Gustafsons
  17. CHIP-48. The assembler is for the HP48 itself - no computer required. A
  18. successful assembly yields a string that may directly be fed to the CHIP
  19. interpreter.
  20.  
  21.  
  22. Introduction:
  23. -------------
  24.  
  25. The method used has its advantages and has its flaws. Let me list the flaws
  26. first:
  27.  
  28. - uncompatible with existing sources:  I have seen several forms of CHIP-8
  29. sources posted here, including Z80-style (syzygy, puzzle15) implemented via
  30. M80 macros, and one more BASIC-like (pong). The approach being described here
  31. is RPN-style and certainly looks weird at first sight.
  32.  
  33. - requires getting used to:  If you've already written CHIP8 using another
  34. assembler, AS48 will require some accustomization due to its unusual syntax
  35. (and mnemonics - but these can be adapted to your gusto)
  36.  
  37. - very rudimentary error handling.
  38.  
  39.  
  40. Lets have a look at the advantages:
  41.  
  42. - relatively fast assembling - considering that there is no machine 
  43.   language involved.
  44. - assembler is relatively small.
  45. - short turn-around: no downloading required. 
  46.   Nice to learn CHIP8 and to test out routines.
  47. - macros, conditional assembly, modules (and more) are possible
  48. - easily modifiable/expandable
  49.  
  50. This sounds like a lot for a tiny assembler, and of course there is a trick
  51. to it. The basic idea is not to write a program that would take a text string
  52. and assemble it, but rather have the source assemble _itself_ :)
  53.  
  54.  
  55. Example
  56. -------
  57.  
  58. A source for AS48 is in fact simply an HP48 user program. Every CHIP-8
  59. mnemonic corresponds to a small subroutine which expects its arguments (the
  60. operands for the CHIP-8 instruction, if any) on the stack, and generates the
  61. corresponding opcode (The CHIP-8 string is built on the stack).  This is the
  62. reason for the RPN-style syntax. Instead of long explanations, lets have a
  63. look at a source in this format:
  64.  
  65. @ --- cut here ---
  66. %%HP: T(3)A(D)F(.);
  67. \<<                        @ User program delimiters
  68.     begin                  @ Start AS48 source
  69. 'start' lbl                @ Define label 'start' (quotes mandatory)
  70.     v0 # FFh movc          @ move const FFh into register v0
  71.     v0 tset                @ set timer with v0
  72. 'loop' lbl                 @ define label 'loop'
  73.     charbuf iset           @ i := charbuf (no quotes required here)
  74.     v0 tget                @ get current timer value into v0
  75.     v0 sbcd                @ store value in v0 as bcd bytes (where i points)
  76.     v2 rreg                @ read registers (via i) up to v2
  77.     v4 # 0h movc           @ zero v4
  78.  
  79.     1 2 START              @* repeat following twice (at assembly-time!)
  80.       0 2 FOR I            @* loop from 0 to 2 (at assembly-time!)
  81.         v3 I #5 * movc     @* v3 := 5 * I (i.e. 0, 5, 10)
  82.         v0 I + ichr        @* point i to character in vI (tricky bit :-)
  83.         v3 v4 # 5h drw     @* draw sprite at i on coords (v3,v4)
  84.       NEXT                 @* inner loop
  85.     NEXT                   @* outer loop 
  86.  
  87.     v0 #0 seqc             @ skip next instruction if v0 == #0
  88.     loop  jmp              @ not timed out yet, continue
  89.     start jmp              @ timed out, restart
  90. 'charbuf' lbl              @ character buffer
  91.     # 0h db                @ define a byte
  92.     # 0h db                @ and another one
  93.     # 0h db                @ and the last one
  94.     end                    @ end AS48 source
  95. \>>
  96. @ --- cut again ---
  97.  
  98. To assemble this source, you would:
  99. - enter it resp. download it to your HP48
  100. - name it (store in a variable)
  101. - give AS48 the quoted variable name as input.
  102. (You may also give AS48 a program as input, but it would be lost after
  103. assembly - just as pressing EVAL with a program in level 1 does)
  104.  
  105. Most of the 'features' listed above directly result from the fact that the
  106. source is a program itself. You can of course use the built-in editor to
  107. enter your sources, take advantage of the function keys as typing aid, have
  108. various sources stored under different names, enter binary, octal or hex
  109. constants ... macros, conditional assembly, and the like result from
  110. this, too. Lets have a closer look:
  111.  
  112. - You'll notice several pseudo-ops, one of them is "lbl" which defines a label.
  113. The corresponding name must precede (of course) and be quoted.
  114. References to labels (i.e. " loop jmp " for ex.) do not require the quotes,
  115. but may have them.
  116.  
  117. - To avoid nameing conflicts, the AS48 mnemonics / pseudo-ops use lower
  118. case. Otherwise, problems with OR, AND, XOR, END ... would have
  119. resulted.
  120.  
  121. - Macros, conditional assembly and the like simply take advantage of the fact
  122. that RPL is available anyway. In the section above marked '*' in the
  123. comments, the outer loop causes all contained code to be generated twice.
  124. The inner loop expands three times, and generates slightly different code
  125. each time. One must keep in mind that such constructs are evaluated and
  126. executed at assembly-time, not at CHIP8 runtime. The outer loop above would
  127. be more elegantly done using a subroutine. (But its still a nice example)
  128.  
  129. - Registers are named 'v0' to 'vf'. Each register in fact simply yields a
  130. binary value corresponding to its number. One _may_ take advantage of this,
  131. as done above in the line marked "tricky bit" ...
  132.  
  133.  
  134. Mnemonics table
  135. ---------------
  136.  
  137. This is basically the opcode table by Andreas Gustafson, I've just added the
  138. AS48 mnemonics and parameters in the left columns. 
  139.  
  140. Note that:  
  141.  
  142. - The 'call 1802 machine code at nnn' is not available as a mnemonic 
  143.  
  144. - there's more mnemonics than the other assemblers have. I have chosen to
  145. keep AS48 as simple as possible, and not to have an "ld" mnemonic with 7
  146. different flavors. If you don't like the mnemonics, just change them. If you
  147. want the same mnemonic for several different opcodes, you'll have to
  148. differentiate according to parameter types, and maybe introduce a bit of your
  149. own syntax. I'm afraid this would (further) slow the beast down. (And spoil
  150. the basically very simple idea behind all this a bit)
  151.  
  152. --- cut here for a quickref chart ---
  153. Parameters Mnem.   Opcode  Description
  154. ---------- -----   ------  ---------------------------------------------
  155.        c   N.A.    0NNN    Call 1802 machine code subroutine at NNN
  156.      x y   mov     8XY0    VX := VY, VF may change
  157.      x c   movc    6XKK    VX := KK
  158.      adr   iset    ANNN    I := NNN
  159.        x   iinc    FX1E    I := I + VX
  160.        x   ichr    FX29    Point I to 5-byte font sprite for hex character VX
  161.        x   tset    FX15    delay_timer := VX
  162.        x   tget    FX07    VX := delay_timer
  163.        x   sset    FX18    sound_timer := VX
  164.        x   kget    FX0A    wait for keypress, store hex value of key in VX
  165.        x   sreg    FX55    Store V0..VX in memory starting at M(I)
  166.        x   rreg    FX65    Read V0..VX from memory starting at M(I)
  167.        x   sbcd    FX33    Store BCD representation of VX in M(I)..M(I+2)
  168.      x c   addc    7XKK    VX := VX + KK
  169.      x y   add     8XY4    VX := VX + VY, VF := carry
  170.      x y   sub     8XY5    VX := VX - VY, VF := not borrow
  171.      x y   subn    8XY7    VX := VY - VX, VF := not borrow
  172.        x   shl     8XYE    VX := VX shl 1, VF := carry
  173.        x   shr     8XY6    VX := VX shr 1, VF := carry
  174.      x y   and     8XY2    VX := VX and VY, VF may change
  175.      x y   or      8XY1    VX := VX or VY, VF may change
  176.      x y   xor     8XY3    VX := VX xor VY, VF may change 
  177.      x c   rnd     CXKK    VX := pseudorandom_number and KK
  178.      adr   jmp     1NNN    Jump to NNN
  179.      adr   jsr     2NNN    Call subroutine at NNN
  180.            rts     00EE    Return from subroutine
  181.      x c   seqc    3XKK    Skip next instruction if VX == KK
  182.      x c   snec    4XKK    Skip next instruction if VX != KK
  183.      x y   seq     5XY0    Skip next instruction if VX == VY
  184.      x y   sne     9XY0    Skip next instruction if VX != VY
  185.        x   skp     EX9E    Skip next instruction if key VX pressed
  186.        x   snkp    EXA1    Skip next instruction if key VX not pressed
  187.      adr   jv0     BNNN    Jump to NNN+V0
  188.            cls     00E0    Clear display
  189.    x y c   drw     DXYN    Show N-byte sprite from M(I) at coords (VX,VY), VF := collision
  190.  
  191. x and y are registers (Use either vn or #n), c is a binary constant,
  192. and adr an (unquoted) label. 
  193.  
  194.       
  195. Pseudo Ops:
  196. -----------
  197.  
  198. The following pseudo-ops are available:
  199.  
  200. Parameter      Pseudo-op    Description
  201. ---------      ---------    -----------
  202.                begin        Start an AS48 source
  203.                end          End an AS48 source
  204. quoted-name    lbl          Define a label.
  205. binary int     dw           deposit a word
  206. binary int     db           deposit a byte
  207. label          aevl         evaluate an address.
  208. --- cut again ---
  209.  
  210. The last one (aevl) requires a bit of explanation. It is required when you're
  211. wanting to do address calculations. Suppose, for example, that you've got:
  212.  
  213. 'table' lbl  @ define label 'table'
  214.    #48h db  
  215.    #50h db  
  216.    #34h db  
  217.    #38h db   @ define some bytes 
  218.  
  219. and want to set register i to the third byte (table+2) 
  220. and want to do this calculation at assembly-time. You'd use:
  221.  
  222.     table aevl #2d + iset
  223.  
  224. The 'aevl' is required here for the forward referencing mechanism to work
  225. correctly. All mnemonics having an address as parameter (jmp, jsr, iset ...)
  226. do this automatically, so it is not required there (that is, if only a label
  227. precedes them). Another example:
  228.  
  229. Compute the difference between labels 'table' and 'endtable', and store into
  230. variable v0:
  231.  
  232.    v0 endtable aevl table aevl - movc
  233.  
  234. Note that two aevl's are required here.
  235.  
  236.  
  237. Listing
  238. -------
  239.  
  240. @ --- cut here ---
  241. %%HP: T(3)A(D)F(.);
  242. DIR
  243.   MNEM DIR
  244. @ register transfer
  245.     movc  \<< # 6000h xc  \>> 
  246.     mov   \<< # 8000h xy  \>> 
  247.     iset  \<< # A000h adr \>> 
  248.     iinc  \<< # F01Eh x   \>> 
  249.     ichr  \<< # F029h x   \>> 
  250.     tset  \<< # F015h x   \>> 
  251.     tget  \<< # F007h x   \>> 
  252.     sset  \<< # F018h x   \>> 
  253.     kget  \<< # F00Ah x   \>> 
  254.     sreg  \<< # F055h x   \>> 
  255.     rreg  \<< # F065h x   \>>
  256.     sbcd  \<< # F033h x   \>>
  257. @ arithmetic
  258.     addc  \<< # 7000h xc  \>>
  259.     add   \<< # 8004h xy  \>>
  260.     sub   \<< # 8005h xy  \>>
  261.     subn  \<< # 8007h xy  \>>
  262.     shl   \<< # 800Eh x   \>>
  263.     shr   \<< # 8006h x   \>>
  264.     and   \<< # 8002h xy  \>>
  265.     or    \<< # 8001h xy  \>>
  266.     xor   \<< # 8003h xy  \>>
  267.     rnd   \<< # C000h xc  \>>
  268. @ flow control
  269.     jmp   \<< # 1000h adr \>>
  270.     jsr   \<< # 2000h adr \>>
  271.     rts   \<< # 00EEh dw  \>>
  272.     seqc  \<< # 3000h xc  \>>
  273.     snec  \<< # 4000h xc  \>>
  274.     seq   \<< # 5000h xy  \>>
  275.     sne   \<< # 9000h xy  \>>
  276.     skp   \<< # E09Eh x   \>>
  277.     sknp  \<< # E0A1h x   \>>
  278.     jv0   \<< # B000h adr \>>
  279. @ miscellaneous
  280.     cls   \<< # 00E0h dw  \>>
  281.     drw   \<< # D000h xyc \>>
  282. @ pseudo ops
  283.     lbl   \<< PC SWAP STO \>>
  284.     db    \<< IF 2 FS? THEN B\->R CHR + ELSE DROP END 
  285.               1 'PC' STO+ PC 3 DISP \>>
  286.     dw    \<< IF 2 FS? THEN B\->R DUP 256 / IP CHR SWAP 256 MOD CHR + + 
  287.               ELSE DROP END 2 'PC' STO+ PC 3 DISP \>>
  288.     aevl  \<< IF 2 FS? THEN EVAL DUP TYPE 6 IF SAME THEN 'UNDEFS' STO+ 
  289.               1 SF # 0h END ELSE DROP #0h END \>>
  290.     SYM DIR @ Directory where assembly actually takes place
  291.     END     @ the 'symbol table' will be created here.
  292.   END
  293.   NEW  \<< \<< begin end \>> \>>
  294.   ASM  \<< RCWS RCLF \-> ws flg \<< DUP 
  295.            "AS48 (G. Kohl)\010Initializing ..." 1 DISP 
  296.            MNEM SYM CLVAR 16 STWS HEX
  297.            #0h v0 STO #1h v1 STO #2h v2 STO #3h v3 STO 
  298.            #4h v4 STO #5h v5 STO #6h v6 STO #7h v7 STO 
  299.            #8h v8 STO #9h v9 STO #Ah va STO #Bh vb STO 
  300.            #Ch vc STO #Dh vd STO #Eh ve STO #Fh vf STO 
  301.            2 CF "PASS 1" 2 DISP EVAL
  302.            2 SF {} 'UNDEFS' STO "PASS 2" 2 DISP EVAL
  303.            IF 1 FS? THEN UNDEFS "Undefd" \->TAG END
  304.            UPDIR UPDIR ws STWS flg STOF \>> \>>
  305.   begin \<< 1 CF #200h 'PC' STO IF 2 FS? THEN "" END \>>
  306.   end   \<< IF 1 FS? THEN DROP END \>>
  307.   xyc   \<< SWAP OR SWAP #10h * OR SWAP SLB OR dw \>>
  308.   x     \<< SWAP SLB OR dw \>>
  309.   xy    \<< SWAP #10h * OR SWAP SLB OR dw \>>
  310.   xc    \<< OR SWAP SLB OR dw \>>
  311.   adr   \<< SWAP aevl OR dw \>>
  312. END
  313. @ --- cut again ---
  314.  
  315.  
  316. Usage 
  317. ----- 
  318.  
  319. Download the above directory as AS48 to your HP48. (Use ascii
  320. transfer, translation code 3). Enter AS48. Hitting NEW will yield an empty
  321. AS48 source (Just \<< begin end \>>). You may now enter the MNEM subdirectory
  322. - the AS48 mnemonics are available as typing aids at this level. Use
  323. [gold][EDIT] to modify the source. When done editing, leave the MNEM
  324. subdirectory ([gold][UP]) and store your source under a name of your choice.
  325. To assemble, enter this name (quoted) and hit ASM. (As an AS48 source is
  326. really an RPL program, you may assemble sources located above ASM in the
  327. directory tree. ASM relies upon being called just above MNEM, though)
  328.  
  329. Successfull assembly will yield a CHIP8 string in level 1. If undefined
  330. symbols exist, a list containing them will instead be returned. There is very
  331. few error checking (to keep this thing reasonably fast), so be careful.
  332. Assembling your source may suddenly stop with an RPL error message if
  333. something's wrong (bad parameter types, for example). If the problem cause
  334. isn't obvious, remember that an AS48 source is really an RPL program: it can
  335. be debugged ...
  336.  
  337.  
  338. Tips & Tricks, Expansions
  339. -------------------------
  340.  
  341. Symbol table: After an assembly, changing to MNEM SYM (and evtl. hitting VAR)
  342. will yield the 'symbol table'. During assembly, every label creates a
  343. variable of that name, the contents of the variable being of course the
  344. CHIP8-adress where it was created. Predefined variables are v0 .. vf (the
  345. CHIP8 registers) and PC. After a successful assembly, PC points to the last
  346. byte (+1) of the generated code, in CHIP8 address space.
  347.  
  348. Macros: Macros may be implemented by creating an RPL program at the start of
  349. your source, naming it, and calling it (with appropriate parameters) in the
  350. rest of the source. Hint: flag 2 is internally used by ASM to keep track if
  351. its on pass 1 or 2. You'll need to create your macro on pass 1 only, so you
  352. might include it into an IF 2 FC? THEN ... END sequence. The macro itself may
  353. also take advantage of flag 2.
  354.  
  355. Modules: a module is but an AS48 source with the 'begin' 'end' brackets
  356. removed. You can then call it from another source. Such modules may reside
  357. higher in the directory tree than ASM.
  358.  
  359. Computed tables: tables of various sorts can be computed using RPL (and some
  360. dw's or db's). An interesting extension might be an RPL program to turn a
  361. GROB into a sprite, or even to directly turn a character (string) into
  362. CHIP8-sprites. 
  363.  
  364. Conversion to a more standard form: The following device could be used to
  365. automatically convert an AS48 source into a format suitable for other
  366. assemblers: in a separate directory, create a small program for every AS48
  367. mnemonic (and pseudo-op) and make it output its own name (in converted form)
  368. as well as its parameters, in prefix form. (Maybe output directly to the
  369. serial port) Then, converting a source (and uploading to a computer) is just a
  370. matter of changing to that directory and evaluating the source.
  371.  ----
  372.  
  373. Hope AS48 can be useful for somebody. This is my first attempt at both
  374. posting in this forum and at writing anything non-trivial in RPL. Please
  375. be indulgent, excuse my mistakes, but tell me about them :-)
  376.  
  377.  
  378.